#ifndef ARRAY_H
#define ARRAY_H
#include "Container_t.h"
//Array_t template class declaration
template<class T> 
class Array_t : public Container_t<T>{
public:
	Array_t();						
	~Array_t();						
	Array_t(const Array_t& a);
	Array_t& operator=(const Array_t& a);
	T* operator[](const int index) const;
	static int getExpandValue();
	static void setExpandValue(const int newValue);
protected:
	void reset();
	T* next();
	T* prev();
	bool appendAction(const T* elem, const int index) throw(bad_alloc);
	bool removeAction(const T* elem);
private:
	static int expandValue;
	T** elements;
	int capacity;
	int pos;
	bool expand_array() throw(bad_alloc);
};

//Array_t template class implementation
template<class T> Array_t<T>::Array_t() : elements(new T*[expandValue]()),capacity(expandValue),pos(0){
	//check if memory allocation falied
	if(elements == 0){
		throw bad_alloc("Cannot create elements in array");
	}
}

template<class T> Array_t<T>::~Array_t(){
	delete[] elements;
}

template<class T> Array_t<T>::Array_t(const Array_t& a){
	//copy data members
	this->capacity = a.capacity;
	this->expandValue = a.expandValue;
	//allocate memory for elements
	int allocSize = ((float)a.count() / (float)expandValue)+1;
	allocSize *= expandValue;
	elements = new T*[allocSize]();
	if(elements == 0){
		throw bad_alloc("Cannot create elements in array");
	}else{
		T* elm = 0;
		for(int i=0;i<a.count();++i){
			insert(a[i]);
		}
	}
	this->pos = a.pos;
}

template<class T> Array_t<T>& Array_t<T>::operator=(const Array_t& a){
	if(this != &a){
		T* elm = 0;
		for(int i=0;i<a.count();++i){
			this->insert(a[i]);
		}
	}
	return *this;
}

template<class T> T* Array_t<T>::operator[](const int index) const{
	if(index >= 0 && index < count()){
		return elements[index];
	}
	throw range_error("Index out of bounds");
}

template<class T> void Array_t<T>::reset(){
	pos = -1;
}

template<class T> T* Array_t<T>::next(){
	T* elm = 0;
	try{
		elm = this->operator [](pos+1);
	}catch(range_error& e){
		return 0;
	}
	if(elm != 0){
		++pos;
		return elm;
	}
	return 0;
}

template<class T> T* Array_t<T>::prev(){
	T* elm = this->operator [](pos-1);
	if(elm != 0){
		--pos;
		return elm;
	}
	return 0;

}

template<class T> bool Array_t<T>::appendAction(const T *elem, const int index) throw(bad_alloc){
	//there is an element at the end that needs right shifting
	if(count()+1 > capacity){
		try{
			expand_array();			
		}catch(bad_alloc& e){
			throw;
		}
	}
	//do right shift, there is space
	for(int i=count()-1;i>=index+1;i--){
		elements[i+1] = elements[i];
	}
	elements[index+1] = (T*)elem;
	return true;
}

template<class T> bool Array_t<T>::removeAction(const T *elem){
	if(pos < 0 || pos >= count()){
		return false;
	}
	//check that pos points to the right element to remove
	if(elem != elements[pos]){
		return false;
	}
	//store element in a temp variable
	T* foundElement = elements[pos];
	//left shift array
	for(int j=pos;j<count()-1;++j){
		elements[j] = elements[j+1];
	}
	elements[count()-1] = 0;
	return true;
}

template<class T> bool Array_t<T>::expand_array() throw(bad_alloc){
	T** temp = new T*[capacity+expandValue]();
	if(temp == 0)
		throw bad_alloc("cannot allocate memory");
	for(int i=0;i<count();i++){
		temp[i] = elements[i];
	}
	delete[] elements;
	elements = temp;
	capacity += expandValue;
	return true;
}
#endif